use std::{collections::BTreeMap, sync::OnceLock};

use bytes::Bytes;
use tokio::sync::{mpsc, RwLock};
use tools::{global_defin::tokio_runtime::get_rt, ResultEx};
use tracing::{error, info, span, warn, Instrument, Level};

use crate::audio_codes::AudioCodes;

use super::file::{WavBuf, WriteWavFile};

static WAV_BUF_CACHE: OnceLock<RwLock<BTreeMap<String, WavBuf>>> = OnceLock::new();

///给出一个指定名称的WavBuf...
/// 名称:一般为文件名.也可以自行取一个不冲突的其他名字.需要写入时单独指定
/// 此方法会使用缓存进行..如果缓存内没有,则会去文件查找
/// 在缓存内找不到时,会将名称增加前缀去文件系统当中查找.前缀由配置的confg().play_bell_path 指定
pub(crate) async fn load_wav_bufs(name: &str) -> ResultEx<WavBuf> {
	let cache = WAV_BUF_CACHE.get_or_init(|| RwLock::new(BTreeMap::new()));
	let fw = cache.read().await;

	match fw.get(name) {
		Some(wb) => Ok(wb.clone()),
		None => {
			drop(fw);
			wav_from_file_to_bufs(name, None).await
		}
	}
}

///给出一个指定名称的WavBuf...
/// 名称:一般为文件名.也可以自行取一个不冲突的其他名字.
/// 此方法会使用缓存进行..如果缓存内没有,则会去文件查找
/// 别名指定了的话,会将对应的buf副本也存入缓存中..即可以使用别名进行查找
/// 在缓存内找不到时,会将名称增加前缀去文件系统当中查找.前缀由配置的confg().play_bell_path 指定
pub(crate) async fn wav_from_file_to_bufs(name: &str, alias: Option<String>) -> ResultEx<WavBuf> {
	let cache = WAV_BUF_CACHE.get_or_init(|| RwLock::new(BTreeMap::new()));
	let mut fw = cache.write().await;

	let wb = WavBuf::from_file(name).await?;
	fw.insert(name.to_owned(), wb.clone());
	if let Some(al) = alias {
		fw.insert(al, wb.clone());
	}

	Ok(wb)
}

///创建并启动一个写入wav文件的协程..此协程只能被启动一次
pub fn start_run_wav_file_control() -> mpsc::UnboundedSender<WavFileControlEvent> {
	static SEND_TO_WAV_FILE_CONTROL: OnceLock<mpsc::UnboundedSender<WavFileControlEvent>> =
		OnceLock::new();
	SEND_TO_WAV_FILE_CONTROL
		.get_or_init(|| {
			let span = span!(Level::ERROR, "wav_write");
			let (s, r) = mpsc::unbounded_channel();
			get_rt().spawn(run_wav_file_manager(r).instrument(span));

			s
		})
		.clone()
}

///处理写入wav文件的协程
async fn run_wav_file_manager(mut recv: mpsc::UnboundedReceiver<WavFileControlEvent>) {
	let mut map = BTreeMap::new();

	loop {
		match recv.recv().await {
			Some(WavFileControlEvent::StartRecordFile(id, file_path, codes, channel_num)) => {
				if map.contains_key(&id) {
					error!("已经存在此id对应的录音文件,关闭前不能再次进行.id:{id}");
					continue;
				}

				match WriteWavFile::new(file_path, codes, channel_num).await {
					Ok(wb) => map.insert(id, wb),
					Err(e) => {
						error!("创建写入对象失败..e:{e}");
						continue;
					}
				};
			}
			Some(WavFileControlEvent::RecordFile(id, is_left, buf)) => match map.get_mut(&id) {
				Some(wb) => {
					if let Err(e) = wb.write_wav_file_buf(&buf, is_left).await {
						error!("写入文件失败..{wb:?}..e:{e}");
					}
				}
				None => error!("当前未找到对应的id值:{id}..无法写入"),
			},
			Some(WavFileControlEvent::StopRecordFile(id)) => {
				if let Some(mut wb) = map.remove(&id) {
					if let Err(e) = wb.write_wav_file_end().await {
						error!("结束一个文件写入异常..{wb:?}..e:{e}");
					}
				} else {
					warn!("不存在此id指定定的wav_buf,id:{id}");
				}
			}
			None => {
				//这里其实是不会走到的,因为有一个发送端一直保存在内存中
				info!("发送端全部退出,退出");
				return;
			}
		}
	}
}

#[derive(Debug)]
pub enum WavFileControlEvent {
	///开始录音,u32为指定的dialog的id..最后一个u8的值1:单声道.2:双声道
	StartRecordFile(u32, String, AudioCodes, u8),
	///发送一个录音的数据,此数据格式对应开始录音时指定的格式.
	/// 目前都是录双声道.
	/// bool值true,代表写入的是左声道.false是右声道.单声道的值为true
	RecordFile(u32, bool, Bytes),
	StopRecordFile(u32),
}
